package com.ustc.hewei.rpc.client.loadbalance;

import com.ustc.hewei.rpc.client.discovery.ServiceDiscovery;
import com.ustc.hewei.rpc.common.protocol.Service;
import lombok.extern.slf4j.Slf4j;

import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.*;

/**
 * @author hewei
 * @version 1.0
 * @description: 包含本地缓存的服务获取类
 * @date 2022/8/10 19:38
 */

@Slf4j
public class ServiceGetter {
    private final ServiceDiscovery serviceDiscovery;

    // 客户端本地服务缓存，对象是由容器注入
    private final ConcurrentHashMap<String, List<Service>> cacheList;

    // 客户端更新缓存列表间隔
    private final int updateTime;

    private final LoadBalanceChoose loadBalanceChoose;

    private final ScheduledExecutorService scheduledExecutorService;

    private final BlockingQueue<Service> invalidService;

    public ServiceGetter(ServiceDiscovery serviceDiscovery, ConcurrentHashMap<String, List<Service>> cacheList, int updateTime, LoadBalanceChoose loadBalanceChoose) {
        this.serviceDiscovery = serviceDiscovery;
        this.cacheList = cacheList;
        this.updateTime = updateTime;
        scheduledExecutorService = Executors.newScheduledThreadPool(1);
        this.loadBalanceChoose = loadBalanceChoose;
        invalidService = new ArrayBlockingQueue<>(1024 * 1024);
    }

    /*
     * @description: 从本地缓存或者是注册中心获取服务列表
     * @param serviceName 服务名称，缓存列表的键
     * @return com.ustc.hewei.rpc.common.protocol.Service
     * @author: hewei
     * @date: 2022/8/10 20:51
     **/
    public Service getService(String serviceName) throws UnknownHostException {
        List<Service> services = cacheList.getOrDefault(serviceName, null);

        // 如果本地的服务列表缓存不存在，此处要加锁，防止多个线程进入去重复创建定时任务
        if (services == null) {
            // 保证同一时间只有一个线程进入
            synchronized (this) {
                // 再次进行判断，如果在等待锁的过程中已经有其他线程写入了，则直接返回结果，不再创建定时任务
                if (cacheList.containsKey(serviceName)) {
                    log.info("客户端本地已存在服务列表缓存！");
                    return loadBalanceChoose.chooseService(serviceName, cacheList.get(serviceName));
                }
                services = serviceDiscovery.getServices(serviceName);
                log.info("从注册中心查询服务列表！");
                cacheList.put(serviceName, services);

                // 开启一个定时任务，定期更新本地缓存
                scheduledExecutorService.scheduleAtFixedRate(() -> {
                    List<Service> serviceList = serviceDiscovery.getServices(serviceName);
                    cacheList.put(serviceName, serviceList);
                    log.info("服务 {} 定时更新成功！", serviceName);
                }, updateTime, updateTime, TimeUnit.SECONDS);
            }
        } else {
            log.info("客户端本地已存在服务列表缓存！");
        }
        return loadBalanceChoose.chooseService(serviceName, services);
    }

    public BlockingQueue<Service> getInvalidService() {
        return invalidService;
    }

    /*
     * @description: 强制从注册中心获取服务列表并刷新本地缓存，返回一个服务
     * @param serviceName
     * @return com.ustc.hewei.rpc.common.protocol.Service
     * @author: hewei
     * @date: 2022/8/10 20:53
     **/
    public void flushListAndGet(Service service) {
        invalidService.offer(service);
    }

    /*
     * @description: 当调用服务失败就会调用此方法，获取下一个服务
     * @param serviceName
     * @param offset 下一个服务的偏移量
     * @return com.ustc.hewei.rpc.common.protocol.Service
     * @author: hewei
     * @date: 2022/8/23 22:06
     **/
    public Service nextService(String serviceName, int offset) {
        List<Service> services = cacheList.getOrDefault(serviceName, null);
        return loadBalanceChoose.nextService(services, serviceName, offset);
    }
}